package cn.xiaoke.damowang_boot_test.util.auto_wrapper;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

abstract class PackField {
    protected Field field;
    protected String fieldName;

    public PackField(Field field) {
        this.field = field;
        this.fieldName = field.getName();
    }

    abstract List<ColumnInfo> getColumnInfoList(Object object);

    abstract Annotation getAnnotation();

    abstract String getGroupName();

    abstract boolean getOuterJoin();

    abstract boolean getInnerJoin();

    Object getValue(Object object) {
        field.setAccessible(true);
        try {
            return field.get(object);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected List<ColumnInfo> createListColumnInfo(Where[] ans, Object value) {
        final List<ColumnInfo> list = new ArrayList<>();
        for (Where where : ans) {
            final String column = where.column();
            final ColumnInfo columnInfo = new ColumnInfo(where.value(), StringUtils.isBlank(column) ? fieldName : column, value, where.join());
            list.add(columnInfo);
        }
        return list;
    }

}

class WherePackField extends PackField {
    private Where an;

    public WherePackField(Field field) {
        super(field);
        an = field.getAnnotation(Where.class);
    }

    @Override
    List<ColumnInfo> getColumnInfoList(Object object) {
        final Where annotation = field.getAnnotation(Where.class);
        final List<ColumnInfo> list = createListColumnInfo(new Where[]{annotation}, getValue(object));
        return list;
    }

    @Override
    Where getAnnotation() {
        return an;
    }

    @Override
    String getGroupName() {
        return fieldName;

    }

    @Override
    boolean getOuterJoin() {
        return false;
    }

    @Override
    boolean getInnerJoin() {
        return false;
    }
}

class WheresPackField extends PackField {
    private Wheres an;

    public WheresPackField(Field field) {
        super(field);
        an = field.getAnnotation(Wheres.class);
    }

    @Override
    List<ColumnInfo> getColumnInfoList(Object object) {

        final Where[] ans = this.getAnnotation().value();
        final Object value = getValue(object);
        final List<ColumnInfo> list = createListColumnInfo(ans, value);
        return list;
    }

    @Override
    Wheres getAnnotation() {
        return an;
    }


    @Override
    String getGroupName() {
        return Optional.of(field.getAnnotation(Wheres.class).group()).filter(StringUtils::isNotBlank).orElse(fieldName);
    }

    @Override
    boolean getOuterJoin() {
        return this.getAnnotation().outerJoin();
    }

    @Override
    boolean getInnerJoin() {
        return this.getAnnotation().innerJoin();
    }
}

class GroupInfoBuilder {
    public static final GroupInfoBuilder INSTANCE = new GroupInfoBuilder();

    private GroupInfoBuilder() {
    }

    public Map<WheresGroupInfo, WheresGroupInfo> buildGroupInfo(List<PackField> packFields, Object obj) {
        final Map<WheresGroupInfo, WheresGroupInfo> map = new TreeMap<>();
        packFields.forEach(packField -> {
            final String groupName = packField.getGroupName();
            final boolean outerJoin = packField.getOuterJoin();
            final boolean innerJoin = packField.getInnerJoin();
            final WheresGroupInfo key = new WheresGroupInfo(groupName, outerJoin, innerJoin);
            final WheresGroupInfo wheresGroupInfo = map.computeIfAbsent(key, (k) -> key);

            if (wheresGroupInfo != key) {
                wheresGroupInfo.innerJoin = key.innerJoin || wheresGroupInfo.innerJoin;
            }

            final List<ColumnInfo> columnInfoList = packField.getColumnInfoList(obj);
            for (ColumnInfo columnInfo : columnInfoList) {
                final String column = columnInfo.column;
                final ColumnGroupInfo columnGroupInfo = wheresGroupInfo.columnGroupInfoMap.computeIfAbsent(column, (k) -> {
                    return new ColumnGroupInfo();
                });
                columnGroupInfo.columnInfos.add(columnInfo);
            }
        });
        return map;
    }
}

interface JoinAndNested {
    default boolean getInnerJoin() {
        return false;
    }

    default boolean getCondition() {
        return false;
    }

}

class WheresGroupInfo implements Comparable<WheresGroupInfo>, JoinAndNested {
    String groupName;
    Map<String, ColumnGroupInfo> columnGroupInfoMap;
    boolean outerJoin;
    boolean innerJoin;

    public WheresGroupInfo(String groupName, boolean outerJoin, boolean innerJoin) {
        this.groupName = groupName;
        this.columnGroupInfoMap = new HashMap<>();
        this.outerJoin = outerJoin;
        this.innerJoin = innerJoin;
    }

    @Override
    public String toString() {
        return "WheresGroupInfo{" +
                "groupName='" + groupName + '\'' +
                ", columnGroupInfoMap=" + columnGroupInfoMap +
                ", outerJoin=" + outerJoin +
                ", innerJoin=" + innerJoin +
                '}';
    }

    @Override
    public int compareTo(WheresGroupInfo group) {
        final int compare = Boolean.compare(this.outerJoin, group.outerJoin);
        return compare != 0 ? compare : this.groupName.compareTo(group.groupName);
    }

    @Override
    public boolean getInnerJoin() {
        return innerJoin;
    }

    @Override
    public boolean getCondition() {
        return columnGroupInfoMap.values().stream().anyMatch(ColumnGroupInfo::getCondition);
    }

}

class ColumnGroupInfo implements JoinAndNested {
    List<ColumnInfo> columnInfos;
    Boolean condition;

    public ColumnGroupInfo() {
        this.columnInfos = new ArrayList<>();
    }

    @Override
    public String toString() {
        return "FieldInfo{" +
                "columnInfos=" + columnInfos +
                '}';
    }

    @Override
    public boolean getInnerJoin() {
        return columnInfos.stream().anyMatch(columnInfo -> columnInfo.join);
    }

    @Override
    public boolean getCondition() {
        return Optional.ofNullable(condition).orElseGet(() -> {
            final boolean flag = columnInfos.stream().anyMatch(ColumnInfo::getCondition);
            this.condition = flag;
            return flag;
        });
    }

}

class ColumnInfo implements JoinAndNested {
    String symbol;
    String column;
    Object value;
    boolean join;

    public ColumnInfo(String symbol, String column, Object value, boolean join) {
        this.symbol = symbol;
        this.column = column;
        this.value = value;
        this.join = join;
    }

    @Override
    public boolean getCondition() {
        return value != null;
    }

    @Override
    public String toString() {
        return "ColumnInfo{" +
                "symbol='" + symbol + '\'' +
                ", column='" + column + '\'' +
                ", value=" + value +
                ", join=" + join +
                '}';
    }
}